﻿// Bakamon Tetris
// Copyright (C) 2011  Martin Cherub

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using SilverlightTetris.BakaTetrisServiceReference;
using System.Collections.ObjectModel;

namespace SilverlightTetris
{
    public partial class MainPage : UserControl
    {
        const int blockInRow = 10; // the number of the block in a rows
        const int initialInterval = 500; // the initial interval of the timer
        const int sizeOfNextPanel = 5;
        int sizeofGrid; // of each grid in pixels
        int[,] Grid = new int[blockInRow, 2 * blockInRow];
        int[,] nextGrid = new int[sizeOfNextPanel, sizeOfNextPanel];
        int[,] permanentBlock = new int[blockInRow, 2 * blockInRow];
        int[,] movingBlock = new int[blockInRow, 2 * blockInRow];
        int blockStartX = (blockInRow / 2) - 1; // the x point where new shape is coming
        int blockStartY = 0; // the y point where new shape is coming
        int blockCurrentX; //the x location of current moving shape
        int blockCurrentY; //the y location of current moving shape
        Shape currentShape;
        Shape nextShape;
        Random random = new Random(DateTime.Now.Millisecond);
        int completedRows; //the number of rows that have been completed
        int nextType; // the next shape in integer
        int currentType; // the current shape type in integer
        int score; // the score of current game
        int level; //the level of current game
        int mode; // the mode of current game
        bool isPaused; //the variable to check if the game is paused
        List<HighScore> HighScoreList;

        // The timer for the application
        System.Windows.Threading.DispatcherTimer timer1 = new System.Windows.Threading.DispatcherTimer();

        BakaTetrisServiceReference.BakaTetrisClient BakaAPI = new BakaTetrisServiceReference.BakaTetrisClient();


        public MainPage()
        {
            InitializeComponent();
            App.Current.Host.Content.Resized += new EventHandler(Content_Resized);
            timer1.Tick += new EventHandler(timer1_Tick);
            mode = 0; // Normal Mode
            sizeofGrid = Convert.ToInt32(canvas1.Width) / blockInRow;
            BakaAPI.GetHighScoreCompleted += new EventHandler<BakaTetrisServiceReference.GetHighScoreCompletedEventArgs>(BakaAPI_GetHighScoreCompleted);
            BakaAPI.AddHighScoreCompleted += new EventHandler<AddHighScoreCompletedEventArgs>(BakaAPI_AddHighScoreCompleted);
            BakaAPI.GetHighScoreAsync();
            
            
        }

        void Content_Resized(object sender, EventArgs e)
        {

            double scale = 0;
            if (App.Current.Host.Content.ActualWidth >= 1024)
                scale = 1;
            else
                scale = 0.75;
            
            UCScale.ScaleX = scale;
            UCScale.ScaleY = scale;

            GridScale.ScaleX = scale;
            GridScale.ScaleY = scale;
            
        }

      
        void BakaAPI_AddHighScoreCompleted(object sender, AddHighScoreCompletedEventArgs e)
        {
            BakaAPI.GetHighScoreAsync();
        }

        void BakaAPI_GetHighScoreCompleted(object sender, BakaTetrisServiceReference.GetHighScoreCompletedEventArgs e)
        {
            HighScoreList = e.Result.ToList();

            if (e.Error != null)
            {
                MessageBox.Show("Leaderboard cannot be loaded");
            }
            else
            {
                List<Scores> CompactList = new List<Scores>();
                foreach (var S in HighScoreList)
                {
                    CompactList.Add(new Scores(S.Name, Convert.ToInt64(S.Score)));
                }

                ScoreGrid.ItemsSource = CompactList;
                
            }
        }

                
        private void newGame()
        {
            generateNextShape();
            blockCurrentX = blockStartX;
            blockCurrentY = blockStartY;
            level = 0;
            completedRows = 0;
            score = 0;
            updateScore();
            clearPermanentBlock();
            clearGrid();
            clearMovingBlock();
            newShape();
            drawPanel();

            BakaAPI.GetHighScoreAsync();

            timer1.Interval = new TimeSpan(0, 0, 0, 0, initialInterval - (level * 50)); // in Milliseconds
                                    
            isPaused = true;
            pauseGame();
            timer1.Start();
            //highestScore.Text = (int)regkey.GetValue("HighestScore") + "";

        }

    
        private void generateNextShape()
        {
            int color = random.Next(1, 8);
            // Insane Mode
            if (mode == 1)
                color = random.Next(1, 3);
            nextType = random.Next(0, 7);
            switch (nextType)
            {
                case 0: nextShape = new Square(color, 0);
                    break;
                case 1: nextShape = new Line(color, random.Next(0, 2));
                    break;
                case 2: nextShape = new RightL(color, random.Next(0, 4));
                    break;
                case 3: nextShape = new LeftL(color, random.Next(0, 4));
                    break;
                case 4: nextShape = new RightZ(color, random.Next(0, 2));
                    break;
                case 5: nextShape = new LeftZ(color, random.Next(0, 2));
                    break;
                case 6: nextShape = new TShape(color, random.Next(0, 4));
                    break;
            }

            drawNextPanel();

        }

        private void drawPanel()
        {
            int column, row;
            //bufferGraphic.Clear(panel1.BackColor);
            canvas1.Children.Clear();
            updateGrid();
            Color color;

            for (row = 0; row < blockInRow * 2; row++)
                for (column = 0; column < blockInRow; column++)
                    if (Grid[column, row] > 0)
                    {


                        if (Grid[column, row] == 1)
                            color = Colors.Red;
                        else if (Grid[column, row] == 2)
                            color = Colors.Black;
                        else if (Grid[column, row] == 3)
                            color = Colors.Blue;
                        else if (Grid[column, row] == 4)
                            color = Colors.Green;
                        else if (Grid[column, row] == 5)
                            color = Colors.Yellow;
                        else if (Grid[column, row] == 6)
                            color = Colors.Orange;
                        else
                            color = Colors.Purple;
                        
                        //Brush brush = new LinearGradientBrush(new Rectangle((column * sizeofGrid) + 1, (row * sizeofGrid) + 1, sizeofGrid - 2, sizeofGrid - 2), Color.WhiteSmoke, color, 60);
                        //bufferGraphic.FillRectangle(brush, (column * sizeofGrid) + 1, (row * sizeofGrid) + 1, sizeofGrid - 2, sizeofGrid - 2);
                        
                        //Random rand = new Random();
                        //byte[] bytes = new byte[4];
                        //rand.NextBytes(bytes);
                        //Color clr = Color.FromArgb(bytes[0], bytes[1], 
                        //                           bytes[2], bytes[3]);
                        GradientStopCollection GradientCollection = new GradientStopCollection();
                        GradientStop Base = new GradientStop();
                        GradientStop Shade = new GradientStop(); ;
                        Shade.Color = Colors.LightGray;
                        Shade.Offset = 0.0;
                        Base.Color = color;
                        Base.Offset = 0.75;
                        GradientCollection.Add(Shade);
                        GradientCollection.Add(Base);

                        Path path = new Path()
                        {
                            Fill = new LinearGradientBrush(GradientCollection,45)
                            
                            
                            ,
                            Data = new RectangleGeometry()
                            {
                                Rect = new Rect((column * sizeofGrid) + 1, (row * sizeofGrid) + 1, sizeofGrid - 2, sizeofGrid - 2)
                            }
                        };

                        canvas1.Children.Add(path);

                    }
            //panelGraphic.DrawImage(bufferBitmap, 0, 0);
        }

        private void updateGrid()
        {
            int column, row;

            clearGrid();
            for (row = 0; row < blockInRow * 2; row++)
                for (column = 0; column < blockInRow; column++)
                {
                    if (permanentBlock[column, row] > 0)
                        Grid[column, row] = permanentBlock[column, row];
                    if (movingBlock[column, row] > 0)
                        Grid[column, row] = movingBlock[column, row];
                }
        }

        //private void panel1_Paint(object sender, PaintEventArgs e)
        //{
        //    drawPanel();
        //    drawNextPanel();
        //}

        private void timer1_Tick(object sender, EventArgs e)
        {
            moveBlock();
            drawPanel();
        }

        private void putShape()
        {
            clearMovingBlock();
            int index;

            for (index = 0; index < 4; index++)
                movingBlock[blockCurrentX + currentShape.blockX[index], blockCurrentY + currentShape.blockY[index]] =
                    currentShape.color;

        }

        private void newShape()
        {
            blockCurrentX = blockStartX;
            blockCurrentY = blockStartY;

            currentType = nextType;

            currentShape = new Square(1, 1);
            switch (currentType)
            {
                case 1: currentShape = new Line(1, 1);
                    break;
                case 2: currentShape = new RightL(1, 1);
                    break;
                case 3: currentShape = new LeftL(1, 1);
                    break;
                case 4: currentShape = new RightZ(1, 1);
                    break;
                case 5: currentShape = new LeftZ(1, 1);
                    break;
                case 6: currentShape = new TShape(1, 1);
                    break;
            }
            int index;
            for (index = 0; index < 4; index++)
            {
                currentShape.blockX[index] = nextShape.blockX[index];
                currentShape.blockY[index] = nextShape.blockY[index];
            }
            currentShape.direction = nextShape.direction;
            currentShape.color = nextShape.color;

            generateNextShape();
        }


        private void moveBlock()
        {
            if (canMoveDown())
            {
                blockCurrentY++;
                putShape();
            }
            else
            {
                int index;
                int endgame = 0;
                for (index = 0; index < 4; index++)
                    permanentBlock[blockCurrentX + currentShape.blockX[index], blockCurrentY + currentShape.blockY[index]] =
                        currentShape.color;
                clearCompletedRow();

                SoundFile1.Stop();
                SoundFile1.Play();

                newShape();
                putShape();
                // Check if game is over
                for (index = 0; index < blockInRow; index++)
                    if (permanentBlock[index, 0] != 0)
                    {
                        endgame = 1;
                    }
                if (endgame == 1)
                {
                    timer1.Stop();
                    MessageBox.Show("GAME OVER");
                    StartBt.Visibility = System.Windows.Visibility.Visible;

                    if (score > HighScoreList.Min(x => x.Score))
                    {
                        NameDialog.Visibility = System.Windows.Visibility.Visible;
                        UsernameTxt.Focus();
                    }
                    //{
                    //    regkey.SetValue("HighestScore", score);
                    //    highestScore.Text = (int)regkey.GetValue("HighestScore") + "";
                    //}
                }
            }

        }

        private void clearCompletedRow()
        {
            int row, column;
            bool completedRow = true;
            for (row = 0; row < (blockInRow * 2); row++)
            {

                if (mode == 0)
                {
                    completedRow = true;
                    for (column = 0; column < blockInRow; column++)
                        if (permanentBlock[column, row] == 0)
                            completedRow = false;
                }
                else if (mode == 1) // insane mode
                {
                    completedRow = false;
                    for (column = 0; column < blockInRow - 4; column++)
                        if (permanentBlock[column, row] != 0 &&
                            (permanentBlock[column, row] == permanentBlock[column + 1, row]) &&
                            (permanentBlock[column + 1, row] == permanentBlock[column + 2, row]) &&
                            (permanentBlock[column + 2, row] == permanentBlock[column + 3, row]) &&
                            (permanentBlock[column + 3, row] == permanentBlock[column + 4, row]))
                            completedRow = true;
                }

                if (completedRow)
                {
                    int rowAbove;

                    for (rowAbove = row - 1; rowAbove >= 0; rowAbove--)
                        for (column = 0; column < blockInRow; column++)
                            permanentBlock[column, rowAbove + 1] = permanentBlock[column, rowAbove];
                    completedRows++;

                    SoundFile2.Stop();
                    SoundFile2.Play();
                    updateScore();
                }
            }
            drawPanel();
            
        }

        private void updateScore()
        {
            score += (level * 10) / (mode + 1);
            scoreLabel.Content = "" + score;
            if ((completedRows % 20) == 0)
                level++;
            levelLabel.Content = "" + level;
            timer1.Interval = new TimeSpan(0, 0, 0, 0, initialInterval - (level * 50));
            linesLabel.Content = completedRows.ToString();
        }

        private bool canMoveDown()
        {
            int index;
            for (index = 0; index < 4; index++)
                if ((blockCurrentY + currentShape.blockY[index] == (2 * blockInRow) - 1) ||
                    (permanentBlock[blockCurrentX + currentShape.blockX[index], blockCurrentY + currentShape.blockY[index] + 1] != 0))

                    return false;

            return true;
        }

        private bool canMoveRight()
        {
            int index;
            for (index = 0; index < 4; index++)
                if ((blockCurrentX + currentShape.blockX[index] == (blockInRow) - 1) ||
                    (permanentBlock[blockCurrentX + currentShape.blockX[index] + 1, blockCurrentY + currentShape.blockY[index]] != 0))

                    return false;

            return true;
        }

        private bool canMoveLeft()
        {
            int index;
            for (index = 0; index < 4; index++)
                if ((blockCurrentX + currentShape.blockX[index] == 0) ||
                    (permanentBlock[blockCurrentX + currentShape.blockX[index] - 1, blockCurrentY + currentShape.blockY[index]] != 0))

                    return false;

            return true;
        }

        private bool canRotate(int direction)
        {
            int index;
            Shape tempShape;
            tempShape = new Square(1, 1);
            switch (currentType)
            {
                case 1: tempShape = new Line(1, 1);
                    break;
                case 2: tempShape = new RightL(1, 1);
                    break;
                case 3: tempShape = new LeftL(1, 1);
                    break;
                case 4: tempShape = new RightZ(1, 1);
                    break;
                case 5: tempShape = new LeftZ(1, 1);
                    break;
                case 6: tempShape = new TShape(1, 1);
                    break;
            }

            for (index = 0; index < 4; index++)
            {
                tempShape.blockX[index] = currentShape.blockX[index];
                tempShape.blockY[index] = currentShape.blockY[index];
            }
            tempShape.direction = currentShape.direction;
            tempShape.Rotate(direction);
            for (index = 0; index < 4; index++)
                if ((blockCurrentX + tempShape.blockX[index] < 0) || (blockCurrentY + tempShape.blockY[index] > (blockInRow * 2) - 1) ||
                    (blockCurrentX + tempShape.blockX[index] > (blockInRow - 1)) ||
                    (permanentBlock[blockCurrentX + tempShape.blockX[index], blockCurrentY + tempShape.blockY[index]] != 0))

                    return false;

            return true;
        }

        private void clearMovingBlock()
        {
            int row, column;
            for (row = 0; row < blockInRow * 2; row++)
                for (column = 0; column < blockInRow; column++)
                    movingBlock[column, row] = 0;
        }

        private void clearPermanentBlock()
        {
            int row, column;
            for (row = 0; row < blockInRow * 2; row++)
                for (column = 0; column < blockInRow; column++)
                    permanentBlock[column, row] = 0;
        }

        private void clearGrid()
        {
            int row, column;
            for (row = 0; row < blockInRow * 2; row++)
                for (column = 0; column < blockInRow; column++)
                    Grid[column, row] = 0;
        }


        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Left && canMoveLeft())
            {
                PlayMoveSound();
                blockCurrentX -= 1;
            }
            else if (e.Key == Key.Right && canMoveRight())
            {
                PlayMoveSound();
                blockCurrentX += 1;
            }
            else if (e.Key == Key.Down)
                timer1.Interval = new TimeSpan(0, 0, 0, 0, 25);
            else if (e.Key == Key.Up && canRotate(1))
                rotateShape('R');
            else if (e.Key == Key.Ctrl && canRotate(-1))
                rotateShape('L');
            else if (e.Key == Key.F2)
                newGame();
            else if (e.Key == Key.F3)
                pauseGame();
            else if (e.Key == Key.F4)
                changeMode();
            putShape();
            drawPanel();
        }

        public void PlayMoveSound()
        {
            SoundFile4.Stop();
            SoundFile4.Play();
        }

        private void changeMode()
        {

            mode = 1 - mode;
            if (mode == 1)
            {
                InsaneLabel1.Visibility = System.Windows.Visibility.Visible;
                InsaneLabel2.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                InsaneLabel1.Visibility = System.Windows.Visibility.Collapsed;
                InsaneLabel2.Visibility = System.Windows.Visibility.Collapsed;
            }

            newGame();
        }

        private void pauseGame()
        {
            if (isPaused == true)
            {
                isPaused = false;
                timer1.Interval = new TimeSpan(0, 0, 0, 0, initialInterval - (level * 50));
                timer1.Start();
                drawPanel();
                //hidePanel.Visible = false;
                pausedLabel.Visibility = Visibility.Collapsed;
            }
            else
            {
                isPaused = true;
                timer1.Stop();
                
                canvas1.Children.Clear();
                //hidePanel.Visible = true;
                pausedLabel.Visibility = Visibility.Visible;
            }

        }

       
        private void rotateShape(char turn)
        {
            SoundFile3.Stop();
            SoundFile3.Play();
            if (turn == 'R')
                currentShape.Rotate(1);
            else
                currentShape.Rotate(-1);
            
        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
                timer1.Interval = new TimeSpan(0, 0, 0, 0, initialInterval - (level * 50));
        }

        private void drawNextPanel()
        {
            //drawPanel();
            
            int index;
            int blockSize = (Convert.ToInt32(nextCanvas.Width) / sizeOfNextPanel);
            int row, column;

            // Clear the next shape grid
            for (row = 0; row < sizeOfNextPanel; row++)
                for (column = 0; column < sizeOfNextPanel; column++)
                    nextGrid[column, row] = 0;

            // Put the shape into the grid
            for (index = 0; index < 4; index++)
                nextGrid[2 + nextShape.blockX[index], 1 + nextShape.blockY[index]] =
                    nextShape.color;
            nextCanvas.Children.Clear();
            Color color;


            for (row = 0; row < sizeOfNextPanel; row++)
                for (column = 0; column < sizeOfNextPanel; column++)
                    if (nextGrid[column, row] > 0)
                    {
                     
                        if (nextGrid[column, row] == 1)
                            color = Colors.Red;
                        else if (nextGrid[column, row] == 2)
                            color = Colors.Black;
                        else if (nextGrid[column, row] == 3)
                            color = Colors.Blue;
                        else if (nextGrid[column, row] == 4)
                            color = Colors.Green;
                        else if (nextGrid[column, row] == 5)
                            color = Colors.Yellow;
                        else if (nextGrid[column, row] == 6)
                            color = Colors.Orange;
                        else
                            color = Colors.Purple;


                        GradientStopCollection GradientCollection = new GradientStopCollection();
                        GradientStop Base = new GradientStop();
                        GradientStop Shade = new GradientStop(); ;
                        Shade.Color = Colors.LightGray;
                        Shade.Offset = 0.0;
                        Base.Color = color;
                        Base.Offset = 0.75;
                        GradientCollection.Add(Shade);
                        GradientCollection.Add(Base);

                        Path path = new Path()
                        {
                            Fill = new LinearGradientBrush(GradientCollection, 45)


                            ,
                            Data = new RectangleGeometry()
                            {
                                Rect = new Rect((column * sizeofGrid) + 1, (row * sizeofGrid) + 1, sizeofGrid - 2, sizeofGrid - 2)
                            }
                        };

                        nextCanvas.Children.Add(path);
                    }
        }

        private void CancelBt_Click(object sender, RoutedEventArgs e)
        {
            NameDialog.Visibility = System.Windows.Visibility.Collapsed;
        }

        private void SubmitBt_Click(object sender, RoutedEventArgs e)
        {
            BakaAPI.AddHighScoreAsync(score, UsernameTxt.Text);
            NameDialog.Visibility = System.Windows.Visibility.Collapsed;
        }

        private void StartBt_Click(object sender, RoutedEventArgs e)
        {
            StartBt.Visibility = System.Windows.Visibility.Collapsed;
            newGame();
        }

        private void NewgameBt_Click(object sender, RoutedEventArgs e)
        {
            newGame();
        }

        private void PauseBt_Click(object sender, RoutedEventArgs e)
        {
            pauseGame();
        }

        private void SwitchmodeBt_Click(object sender, RoutedEventArgs e)
        {
            changeMode();
        }
    }

    
}
